﻿using System;
using System.Collections;
using System.Linq;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using Microsoft.ComponentModel.Composition.Extensions.Web.Internal;

namespace Microsoft.ComponentModel.Composition.Extensions.Web
{
    public class ScopedContainerManager
    {
        private readonly ComposablePartCatalog _rootCatalog;
        private readonly CompositionContainer _container;
        private const string MefContainerKey = "mef-container";
        private const string ModeKey = "Mode";

        public ScopedContainerManager(ComposablePartCatalog rootCatalog)
        {
            if (rootCatalog == null) throw new ArgumentNullException("rootCatalog");

            _rootCatalog = rootCatalog;

            _container = new CompositionContainer(new FilteredCatalog(rootCatalog, def => GetAllWithinAppScope(def)), true);
        }

        public CompositionContainer ApplicationContainer
        {
            get { return _container; }
        }

        //TODO: thread safety?
        public void CreateRequestContainer(IDictionary dictionary)
        {
            if (dictionary == null) throw new ArgumentNullException("dictionary");

            if (!dictionary.Contains(MefContainerKey))
            {
                var catalog = new FilteredCatalog(_rootCatalog, def => GetAllWithinRequestScope(def));
                var requestContainer = new CompositionContainer(catalog, false, ApplicationContainer);

                dictionary[MefContainerKey] = requestContainer;
            }
        }

        public void EndRequestContainer(IDictionary dictionary)
        {
            if (dictionary == null) throw new ArgumentNullException("dictionary");

            CompositionContainer requestContainer = (CompositionContainer)dictionary[MefContainerKey];

            if (requestContainer != null)
            {
                requestContainer.Dispose();
            }

            dictionary.Remove(MefContainerKey);
        }

        public CompositionContainer GetRequestContainer(IDictionary dictionary)
        {
            if (dictionary == null) throw new ArgumentNullException("dictionary");

            CompositionContainer requestContainer = (CompositionContainer)dictionary[MefContainerKey];

            if (requestContainer == null)
            {
                CreateRequestContainer(dictionary);

                requestContainer = (CompositionContainer)dictionary[MefContainerKey];
            }

            return requestContainer;
        }

        public void Close()
        {
            if (_container != null)
                _container.Dispose();

            if (_rootCatalog != null)
                _rootCatalog.Dispose();
        }

        private static bool GetAllWithinAppScope(ComposablePartDefinition def)
        {
            return def.ExportDefinitions.
                Any(ed =>
                    !ed.Metadata.ContainsKey(ModeKey) ||
                    (ed.Metadata.ContainsKey(ModeKey) && ((WebScopeMode)ed.Metadata[ModeKey]) == WebScopeMode.Application));
        }

        private static bool GetAllWithinRequestScope(ComposablePartDefinition def)
        {
            return def.ExportDefinitions.
                Any(ed =>
                    (ed.Metadata.ContainsKey(ModeKey) && ((WebScopeMode)ed.Metadata[ModeKey]) == WebScopeMode.Request));
        }
    }
}
